package com.hy.mybatisplus.exception;

import com.hy.mybatisplus.entity.R;
import org.mybatis.spring.MyBatisSystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 * @author hy
 * @date 2020/09/29  9:30 AM
 */
@RestControllerAdvice(basePackages = {"com.hy.mybatisplus.controller"})
//public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public class GlobalExceptionHandler {
    protected static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    /**
     * 定义要捕获的异常 可以多个 @ExceptionHandler({})
     *
     * @param request  request
     * @param e        exception
     * @param response response
     * @return 响应结果
     */
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<?> customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
        try {
            TransactionAspectSupport.currentTransactionStatus()
                    .setRollbackOnly();
        } catch (NoTransactionException ex) {
            ex.printStackTrace();
        }
        e.printStackTrace();
//        response.setStatus(HttpStatus.BAD_REQUEST.value());
        R error = R.error(ErrorEnums.SERVER_ERROR.getCode(), e.getMessage());
        error.put("errorMsg", e.getMessage());
        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader())
                .body(error);
    }

    /**
     * 捕获  RuntimeException 异常
     * 如果你觉得在一个 exceptionHandler 通过  if (e instanceof xxxException) 太麻烦
     * 那么你还可以自己写多个不同的 exceptionHandler 处理不同异常
     *
     * @param request  request
     * @param e        exception
     * @param response response
     * @return 响应结果
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<?> runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
        e.printStackTrace();
        R error = R.error(501, "服务器出现错误!");
//        response.setStatus(HttpStatus.BAD_REQUEST.value());
        if (e instanceof MyBatisSystemException) {
            error.put("msg", "sql异常，请检查").put("errorMsg", e.getMessage());
            return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader())
                    .body(error);
        } else if (e instanceof MaxUploadSizeExceededException) {
            error.put("msg", "文件上传超出最大限制!").put("errorMsg", e.getMessage());
            return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader())
                    .body(error);
        }
        error.put("errorMsg", e.getMessage());
        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader())
                .body(error);
    }


    /**
     * DataAccessException异常处理
     *
     * @param e {@link DataAccessException}
     * @return responseEntity
     */
    @ExceptionHandler(value = DataAccessException.class)
    public ResponseEntity<?> handleDataAccessException(DataAccessException e) {
        TransactionAspectSupport.currentTransactionStatus()
                .setRollbackOnly();
        e.printStackTrace();
        R response = R.error(ErrorEnums.SQL_ERROR.getCode(), ErrorEnums.SQL_ERROR.getErrorInfo()).put("errorMsg", e.getMessage());

        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader())
                .body(response);
    }


    /**
     * DataAccessException异常处理
     *
     * @param e {@link DataAccessException}
     * @return responseEntity
     */
    @ExceptionHandler(value = org.springframework.dao.DataAccessException.class)
    public ResponseEntity<?> handleDataAccessException(org.springframework.dao.DataAccessException e) {
        TransactionAspectSupport.currentTransactionStatus()
                .setRollbackOnly();
        e.printStackTrace();

        R response = R.error(ErrorEnums.SQL_ERROR.getCode(), ErrorEnums.SQL_ERROR.getErrorInfo()).put("errorMsg", e.getMessage());

        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader())
                .body(response);
    }

    /**
     * BindException异常处理,javax.validation 参数绑定异常
     *
     * @param e {@link BindException}
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    public ResponseEntity<?> handleBindException(BindException e) {
        e.printStackTrace();

        List<FieldError> errors = e.getFieldErrors();
        List<String> fields = errors
                .stream()
                .map(fieldError -> fieldError.getField() + ":" + fieldError.getDefaultMessage())
                .collect(Collectors.toList());
        R error = R.error(ErrorEnums.BIND_ERROR.getCode(), ErrorEnums.BIND_ERROR.getErrorInfo()).put("errorMsg", fields);

        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader()).body(error);
    }

    /**
     * BindException异常处理,javax.validation 参数绑定异常
     *
     * @param e {@link MethodArgumentNotValidException}
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        e.printStackTrace();

        List<FieldError> errors = e.getBindingResult().getFieldErrors();
        List<String> fields = errors
                .stream()
                .map(fieldError -> fieldError.getField() + ":" + fieldError.getDefaultMessage())
                .collect(Collectors.toList());
        R error = R.error(ErrorEnums.BIND_ERROR.getCode(), ErrorEnums.BIND_ERROR.getErrorInfo()).put("errorMsg", fields);

        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader()).body(error);
    }

    /**
     * Exception异常处理,所有未指定handle异常
     *
     * @param e {@link Exception}
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<?> handleException(Exception e) {
        e.printStackTrace();

        R error = R.error(ErrorEnums.SERVER_ERROR.getCode(), ErrorEnums.SERVER_ERROR.getErrorInfo()).put("errorMsg", e.getMessage());

        return ResponseEntity.status(HttpStatus.OK).headers(jsonHeader()).body(error);
    }

//ihou
//    /**
//     * 通用的接口映射异常处理方
//     */
//    @Override
//    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers,
//                                                             HttpStatus status, WebRequest request) {
//        ex.printStackTrace();
//        if (ex instanceof MethodArgumentNotValidException) {
//            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
//            return new ResponseEntity<>(new ErrorResponseEntity(status.value(), exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status);
//        }
//        if (ex instanceof MethodArgumentTypeMismatchException) {
//            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
//            logger.error("参数转换失败，方法：" + exception.getParameter().getMethod().getName() + "，参数：" + exception.getName()
//                    + ",信息：" + exception.getLocalizedMessage());
//            return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "参数转换失败"), status);
//        }
//        return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "参数转换失败"), status);
//    }


    /**
     * 设置Http头的contentType为Json
     *
     * @return httpHeaders
     */
    private HttpHeaders jsonHeader() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return httpHeaders;
    }
}
